package com.dd.cloud.auth.filter;

import com.dd.cloud.auth.config.CorsProperties;
import com.dd.cloud.auth.contants.RoleConstants;
import com.dd.cloud.auth.entity.RoleMenu;
import com.dd.cloud.auth.security.TokenManager;
import com.dd.cloud.auth.service.IAdminPowerService;
import com.dd.cloud.common.constants.HealthConstants;
import com.dd.cloud.common.constants.RedisKeyConstants;
import com.dd.cloud.common.enums.ZkExceptionEnum;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.utils.JsonUtils;
import com.dd.cloud.common.vo.LoginInfoTransfer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.*;

/**
 * <p>
 * 访问过滤器, 权限授权处理
 * </p>
 *
 * @author qy
 * @since 2019-11-08
 */
@Slf4j
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
    private RedisTemplate redisTemplate;
    private CorsProperties corsProperties;
    private IAdminPowerService adminPowerService;

    public TokenAuthenticationFilter(AuthenticationManager authManager,
                                     RedisTemplate redisTemplate, CorsProperties corsProperties, IAdminPowerService adminPowerService) {
        super(authManager);
        this.redisTemplate = redisTemplate;
        this.corsProperties = corsProperties;
        this.adminPowerService = adminPowerService;
    }

    /**
     * @param req:
     * @param res:
     * @param chain:
     * @return void
     * @description 授权
     * @author czg
     * @date 2023/6/7 10:43
     */
    @Override
    protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        log.debug("=================" + req.getRequestURI());

        String uri = req.getParameter("uri");
        if (StringUtils.isBlank(uri)){
            uri = req.getRequestURI();
        }
        log.info("授权的url:{}", uri);
        UsernamePasswordAuthenticationToken authentication = null;
        try {
            authentication = getAuthentication(req, uri);
        } catch (ZekeException e) {
            log.info("当前uri没有权限");
            e.printStackTrace();
            throw e;
        }

        if (authentication != null) {
            SecurityContextHolder.getContext().setAuthentication(authentication);
        } else {
            log.info("当前uri没有权限");
            throw new ZekeException(ZkExceptionEnum.NO_ERMSSION);
        }
        chain.doFilter(req, res);
    }

    /**
     * @param request:
     * @return UsernamePasswordAuthenticationToken
     * @description 从request中获取认证信息, 并从redis中获取权限
     * @author czg
     * @date 2023/6/7 10:41
     */
    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request, String uri) {
        //登录信息存放于header中
        String loginTransfer = request.getHeader(HealthConstants.CommonConstants.LOGIN_INFO);
        if (loginTransfer == null || StringUtils.isBlank(loginTransfer)) {
            request.setAttribute("error", ZkExceptionEnum.LOGIN_AGAIN.getMsg());
            throw new ZekeException(ZkExceptionEnum.LOGIN_AGAIN);
        }
        try {
            loginTransfer = URLDecoder.decode(loginTransfer, "utf-8");

        } catch (Exception e) {
            e.printStackTrace();
        }
        LoginInfoTransfer loginInfoTransfer = JsonUtils.strToObject(loginTransfer, LoginInfoTransfer.class);
        if (loginInfoTransfer == null || loginInfoTransfer == null) {
            request.setAttribute("error", ZkExceptionEnum.LOGIN_AGAIN.getMsg());
            throw new ZekeException(ZkExceptionEnum.LOGIN_AGAIN);
        }
        if (loginInfoTransfer.getAllowPath() && loginInfoTransfer.getLoginInfo() == null) {
            request.setAttribute("error", ZkExceptionEnum.LOGIN_AGAIN.getMsg());
            throw new ZekeException(ZkExceptionEnum.LOGIN_AGAIN);
        }

        /**
         * 此处逻辑,
         * 1,请求不是到auth服务的,只用auth去验证是否有权限, 要验证的uri就存放于req的parameter中
         * 2,请求就是到auth服务的,会自动验证权限,此时要验证的uri就可以直接用req获取
         */
        List<RoleMenu> roleMenuByType = adminPowerService.getRoleMenuByType(loginInfoTransfer.getLoginInfo().getType());
        List<String> collect = new ArrayList<>();
        //获取用户权限
        for (RoleMenu menu:
                roleMenuByType) {
            if (!StringUtils.isBlank(menu.getServerUrl())){
                if (menu.getServerUrl().indexOf(",")>=0){
                    collect.addAll( Arrays.asList(menu.getServerUrl().split(",")));
                } else {
                    collect.add(menu.getServerUrl());
                }
            }
        }
        if (!collect.contains(uri) && !corsProperties.getAllowPath().contains(uri)){
            //当前uri不在管控范围内, 即未添加为菜单url或白名单url, 是未知的url
            request.setAttribute("error", ZkExceptionEnum.UNKNOWN_URL.getMsg());
            throw new ZekeException(ZkExceptionEnum.UNKNOWN_URL);
        }

        //当前用户的权限url就存放于redis, key就是权限id
        String key = RedisKeyConstants.CacheKey.AUTHROLEMENU + loginInfoTransfer.getLoginInfo().getRoleId();
        List<String> permissionValueList = (List<String>) redisTemplate.opsForValue().get(key);
        if (!permissionValueList.contains(uri) && !corsProperties.getAllowPath().contains(uri)){
            //当前uri没有权限, 而且不在uri白名单中,授权失败
            return null;
        }
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        for (String permissionValue : permissionValueList) {
            if (StringUtils.isEmpty(permissionValue)) {
                continue;
            }
            SimpleGrantedAuthority authority = new SimpleGrantedAuthority(permissionValue);
            authorities.add(authority);
        }

        if (!StringUtils.isEmpty(loginTransfer)) {
            return new UsernamePasswordAuthenticationToken(loginTransfer, "", authorities);
        }
        return null;

    }
}